#include "StdAfx.hpp"
#include "VariableTypes.hpp"
#include "Framework.hpp"
#include <shlobj.h> // For SHBrowseForFolder

using namespace common;


////////////////////////////////////////////////////////////////////////////////
// struct BrowseableFileType

bool BrowseableFileType::ShowOpenDialog(string &outFilePath, BrowseableFileType *fileType)
{
	outFilePath.clear();

	char filePathBuf[MAX_PATH];
	if (fileType)
		strcpy_s(filePathBuf, _countof(filePathBuf), fileType->LastFilePath.c_str());
	else
		ClearStr(filePathBuf);

	string lastFileDir;
	if (fileType && fileType->LastFilePath.empty())
		ExtractFilePath(&lastFileDir, fileType->LastFilePath);

	OPENFILENAME ofn = {
		sizeof(OPENFILENAME), // struct size
		frame::Wnd, // owner
		frame::Instance, // instance
		fileType && !fileType->Filter.empty() ? fileType->Filter.c_str() : NULL, // filter
		NULL, // custom filter
		0, // custom filter max
		fileType ? fileType->LastFilterIndex : 0,
		filePathBuf,
		_countof(filePathBuf),
		NULL, // file title
		0, // max file title
		!lastFileDir.empty() ? lastFileDir.c_str() : NULL, // initial dir
		fileType && !fileType->Title.empty() ? fileType->Title.c_str() : NULL, // title
		OFN_ENABLESIZING | OFN_EXPLORER | OFN_FILEMUSTEXIST | OFN_HIDEREADONLY | OFN_LONGNAMES | OFN_PATHMUSTEXIST, // flags
		0, // file offset - ??
		0, // file extension - ??
		fileType && !fileType->DefaultExtension.empty() ? fileType->DefaultExtension.c_str() : NULL,
		NULL, // custom data
		NULL, // hook
		NULL, // template name
	};
	
	if (GetOpenFileName(&ofn))
	{
		if (fileType)
		{
			fileType->LastFilePath = filePathBuf;
			fileType->LastFilterIndex = ofn.nFilterIndex;
		}
		outFilePath = filePathBuf;
		return !outFilePath.empty();
	}
	else
		return false;
}

bool BrowseableFileType::ShowSaveDialog(string &outFilePath, BrowseableFileType *fileType)
{
	outFilePath.clear();

	char filePathBuf[MAX_PATH];
	if (fileType)
		strcpy_s(filePathBuf, _countof(filePathBuf), fileType->LastFilePath.c_str());
	else
		ClearStr(filePathBuf);

	string lastFileDir;
	if (fileType && fileType->LastFilePath.empty())
		ExtractFilePath(&lastFileDir, fileType->LastFilePath);

	OPENFILENAME ofn = {
		sizeof(OPENFILENAME), // struct size
		frame::Wnd, // owner
		frame::Instance, // instance
		fileType && !fileType->Filter.empty() ? fileType->Filter.c_str() : NULL, // filter
		NULL, // custom filter
		0, // custom filter max
		fileType ? fileType->LastFilterIndex : 0,
		filePathBuf,
		_countof(filePathBuf),
		NULL, // file title
		0, // max file title
		!lastFileDir.empty() ? lastFileDir.c_str() : NULL, // initial dir
		fileType && !fileType->Title.empty() ? fileType->Title.c_str() : NULL, // title
		OFN_ENABLESIZING | OFN_EXPLORER | OFN_OVERWRITEPROMPT | OFN_HIDEREADONLY | OFN_LONGNAMES | OFN_PATHMUSTEXIST, // flags
		0, // file offset - ??
		0, // file extension - ??
		fileType && !fileType->DefaultExtension.empty() ? fileType->DefaultExtension.c_str() : NULL,
		NULL, // custom data
		NULL, // hook
		NULL, // template name
	};

	if (GetOpenFileName(&ofn))
	{
		if (fileType)
		{
			fileType->LastFilePath = filePathBuf;
			fileType->LastFilterIndex = ofn.nFilterIndex;
		}
		outFilePath = filePathBuf;
		return !outFilePath.empty();
	}
	else
		return false;
}


////////////////////////////////////////////////////////////////////////////////
// class EnumType

bool EnumType::NameToValue(uint &outValue, const string &name) const
{
	uint count = GetCount();
	string s;
	for (uint i = 0; i < count; i++)
	{
		GetName(s, i);
		if (name == s)
		{
			outValue = GetValue(i);
			return true;
		}
	}

	outValue = 0xffffffff;
	return false;
}

bool EnumType::ValueToName(string &outName, uint value) const
{
	uint count = GetCount();
	for (uint i = 0; i < count; i++)
	{
		if (value == GetValue(i))
		{
			GetName(outName, i);
			return true;
		}
	}

	outName.clear();
	return false;
}


////////////////////////////////////////////////////////////////////////////////
// class CharVariable

void CharVariable::LoadText(IParamObject &obj, common::Tokenizer &tokenizer)
{
	tokenizer.AssertToken(Tokenizer::TOKEN_STRING);
	const string &s = tokenizer.GetString();
	if (s.length() != 1)
		tokenizer.CreateError(__FUNCTION__);
	SetValue(obj, s[0]);
	tokenizer.Next();
}

void CharVariable::SaveText(const IParamObject &obj, TokenWriter &tokWriter)
{
	string s;
	s.resize(1);
	GetValue(obj, s[0]);
	tokWriter.WriteString(s);
}

void CharVariable::LoadFromString(IParamObject &obj, const string &s)
{
	if (s.length() != 0)
		throw Error(__FUNCTION__);
	SetValue(obj, s[0]);
}

void CharVariable::SaveToString(const IParamObject &obj, string &out)
{
	out.resize(1);
	GetValue(obj, out[0]);
}


////////////////////////////////////////////////////////////////////////////////
// class BoolVariable

void BoolVariable::LoadText(IParamObject &obj, common::Tokenizer &tokenizer)
{
	tokenizer.AssertToken(Tokenizer::TOKEN_IDENTIFIER);
	bool v;
	if (tokenizer.GetString() == "false")
		v = false;
	else if (tokenizer.GetString() == "true")
		v = true;
	else
		tokenizer.CreateError("Invalid boolean value.");
	tokenizer.Next();
	SetValue(obj, v);
}

void BoolVariable::SaveText(const IParamObject &obj, TokenWriter &tokWriter)
{
	bool v;
	GetValue(obj, v);
	tokWriter.WriteIdentifier(v ? "true" : "false");
}

void BoolVariable::LoadFromString(IParamObject &obj, const string &s)
{
	bool v;
	MustStrToSth<bool>(&v, s);
	SetValue(obj, v);
}

void BoolVariable::SaveToString(const IParamObject &obj, string &out)
{
	bool v;
	GetValue(obj, v);
	SthToStr<bool>(&out, v);
}


////////////////////////////////////////////////////////////////////////////////
// class StlStringVariable

void StlStringVariable::LoadBinary(IParamObject &obj, common::Stream &stream)
{
	string s;
	stream.ReadString4(&s);
	SetValue(obj, s);
}

void StlStringVariable::SaveBinary(const IParamObject &obj, common::Stream &stream)
{
	string s;
	GetValue(obj, s);
	stream.WriteString4(s);
}

void StlStringVariable::LoadText(IParamObject &obj, common::Tokenizer &tokenizer)
{
	tokenizer.AssertToken(Tokenizer::TOKEN_STRING);
	SetValue(obj, tokenizer.GetString());
	tokenizer.Next();
}

void StlStringVariable::SaveText(const IParamObject &obj, TokenWriter &tokWriter)
{
	string v;
	GetValue(obj, v);
	tokWriter.WriteString(v);
}

void StlStringVariable::LoadFromString(IParamObject &obj, const string &s)
{
	SetValue(obj, s);
}

void StlStringVariable::SaveToString(const IParamObject &obj, string &out)
{
	GetValue(obj, out);
}

void StlStringVariable::ToShortString(const IParamObject &obj, string &out)
{
	GetValue(obj, out);
	if (out.length() > 32)
	{
		out.erase(29);
		out.append("...");
	}
}


////////////////////////////////////////////////////////////////////////////////
// class DirPathVariable

bool DirPathVariable::ShowDirDialog(string &inoutDir, const string &title)
{
	char sz[MAX_PATH];
	strcpy_s(sz, _countof(sz), inoutDir.c_str());

	BROWSEINFO bi = {
		frame::Wnd, // owner
		NULL, // root
		sz, // display name
		title.c_str(),
		BIF_USENEWUI | BIF_RETURNONLYFSDIRS, // flags
		NULL, // fn
		NULL, // lparam
		0, // image
	};

	PIDLIST_ABSOLUTE pidList = SHBrowseForFolder(&bi);
	if (pidList)
	{
		BOOL ok = SHGetPathFromIDList(pidList, sz);
		CoTaskMemFree(pidList);
		if (ok)
		{
			inoutDir = sz;
			return true;
		}
		else
			return false;
	}
	else
		return false;
}


////////////////////////////////////////////////////////////////////////////////
// class EnumVariable

void EnumVariable::LoadText(IParamObject &obj, common::Tokenizer &tokenizer)
{
	uint value;
	if (tokenizer.QueryToken(Tokenizer::TOKEN_STRING))
	{
		if (!m_EnumType || !m_EnumType->NameToValue(value, tokenizer.GetString()))
			tokenizer.CreateError("Invalid enum value: \"" + tokenizer.GetString() + "\".");
		tokenizer.Next();
		SetValue(obj, value);
	}
	else
		__super::LoadText(obj, tokenizer);
}

void EnumVariable::SaveText(const IParamObject &obj, TokenWriter &tokenizer)
{
	uint value;
	GetValue(obj, value);
	string s;
	if (m_EnumType && m_EnumType->ValueToName(s, value))
		tokenizer.WriteString(s);
	else
		__super::SaveText(obj, tokenizer);
}

void EnumVariable::LoadFromString(IParamObject &obj, const string &s)
{
	uint value;
	if (!m_EnumType || !m_EnumType->NameToValue(value, s))
		__super::LoadFromString(obj, s);
	SetValue(obj, value);
}

void EnumVariable::SaveToString(const IParamObject &obj, string &out)
{
	uint value;
	GetValue(obj, value);
	if (!m_EnumType || !m_EnumType->ValueToName(out, value))
		__super::SaveToString(obj, out);
}


////////////////////////////////////////////////////////////////////////////////
// class TimeVariable

void TimeVariable::TimeToStr(string &out, GameTime v)
{
	v.ToTimespan().ToString(&out, true);
}

bool TimeVariable::StrToTime(GameTime &out, const string &s)
{
	size_t colon1 = s.find(':');
	if (colon1 == string::npos) return false;
	size_t colon2 = s.find(':', colon1 + 1);
	if (colon2 == string::npos) return false;
	size_t colon3 = s.find(':', colon2 + 1);
	if (colon3 == string::npos) return false;

	int8 hour;
	uint min, sec, msec;

	if (StrToInt(&hour, s.substr(0, colon1)) != 0)
		return false;
	if (StrToUint(&min, s.substr(colon1+1, colon2-colon1-1)) != 0)
		return false;
	if (StrToUint(&sec, s.substr(colon2+1, colon3-colon2-1)) != 0)
		return false;
	if (StrToUint(&msec, s.substr(colon3+1)) != 0)
		return false;

	int8 allMsec = msec + sec*1000 + min*(1000*60);
	if (hour >= 0)
		allMsec += hour*(1000*60*60);
	else
		allMsec = hour*(1000*60*60) - allMsec;

	out = MillisecondsToGameTime(allMsec);
	return true;
}

void TimeVariable::LoadText(IParamObject &obj, common::Tokenizer &tokenizer)
{
	tokenizer.AssertToken(Tokenizer::TOKEN_STRING);
	GameTime v;
	if (!StrToTime(v, tokenizer.GetString()))
		tokenizer.CreateError("Invalid time value.");
	tokenizer.Next();
	SetValue(obj, v);
}

void TimeVariable::SaveText(const IParamObject &obj, TokenWriter &tokenizer)
{
	GameTime v;
	GetValue(obj, v);
	
	string s;
	TimeToStr(s, v);

	tokenizer.WriteString(s);
}

void TimeVariable::LoadFromString(IParamObject &obj, const string &s)
{
	GameTime v;
	if (!StrToTime(v, s))
	{
		v = GameTime::ZERO;
		//throw Error("Invalid time value.", __FILE__, __LINE__);
	}
	SetValue(obj, v);
}

void TimeVariable::SaveToString(const IParamObject &obj, string &out)
{
	GameTime v;
	GetValue(obj, v);
	TimeToStr(out, v);
}
